﻿
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using MacomberMapSystem.Common.Database;
using System.IO;
using MacomberMapSystem.Common.Serialization;
using System.Data.Common;
using MacomberMapSystem.Common.Internals;
using System.Text.RegularExpressions;
using System.IO.Compression;
using MacomberMapSystem.Common.Processing;
using System.Xml;
using System.Threading;
using MacomberMapSystem.Common.Properties;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;
using MacomberMapSystem.Common.Integration;
using MacomberMapSystem.Common.CIM.Direct;
using MacomberMapSystem.Common.Exports;
using System.Drawing.Imaging;
using MacomberMapSystem.Common.Integration.Blackstart;
#if DotNetOracle
using System.Data.OracleClient;
#else
    using Oracle.DataAccess.Client;
#endif

namespace MacomberMapSystem.Common.User_Interfaces.Database
{
    /// <summary>
    /// This form allows the system to add in a model
    /// </summary>
    public partial class frmModelMaintenance : DockContent
    {
        #region Variable declarations
        /// <summary>The thread for our process manager</summary>
        private Thread ProcessManager = null;

        /// <summary>Our collection of named objects</summary>
        private Dictionary<String, Object> NamedObjects = new Dictionary<string, object>();

        /// <summary>The currently active model</summary>
        private MM_Database_Model Model;

        /// <summary>Our collection of processors</summary>
        private List<CIM_Processor> Processors = new List<CIM_Processor>();

        /// <summary>Our repository</summary>
        private MM_Repository Repository;

        /// <summary>The path to our selected model</summary>
        private String SelectedModel;
     
        /// <summary>Our collection of models</summary>
        public Dictionary<int, MM_Database_Model> Models;

        /// <summary>The database connector</summary>
        private MM_Database_Connector Db;
        #endregion

        /// <summary>
        /// Initialize our model addition module
        /// </summary>
        /// <param name="Repository"></param>
        /// <param name="Models"></param>        
        /// <param name="Db"></param>
        public frmModelMaintenance(MM_Repository Repository, Dictionary<int, MM_Database_Model> Models, MM_Database_Connector Db)
        {
            this.Repository = Repository;
            InitializeComponent();

            Repository.BuildModelCombo(cmbLastModel);

            this.Db = Db;
            this.Repository = Repository;
            this.Models = Models;
           

            //Propigate our list of known CIM classes
            List<String> ModelNames = new List<string>();
            using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT UNIQUE(ModelClass) FROM MM_DATABASE_MODEL"))
            using (DbDataReader dRd = dCmd.ExecuteReader())
                while (dRd.Read())
                    ModelNames.Add(dRd["ModelClass"].ToString());

            using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT UNIQUE(ModelCategory) FROM MM_DATABASE_MODEL"))
            using (DbDataReader dRd = dCmd.ExecuteReader())
                while (dRd.Read())
                {
                    cmbModelCategory.Items.Add(dRd["ModelCategory"].ToString());
                    cmbModelCategory2.Items.Add(dRd["ModelCategory"].ToString());
                }
            cmbModelCategory.Items.Add("Direct");
            cmbModelCategory2.Items.Add("Direct");

            ModelNames.Sort(new InverseStringSorter());
            cmbModelClass.Sorted = false;
            cmbModelClass.Items.AddRange(ModelNames.ToArray());

            //Load in our collection of processors     
            int TopLocation = btnOk.Bottom + 9;
            foreach (XmlElement xElem in Repository.xConfig.SelectNodes("Configuration/Processor"))
            {
                Type FoundType = MM_Type_Locator.LocateType("MacomberMapSystem.Common.Processing." + xElem.Attributes["Module"].Value, null);
                if (FoundType == null)
                    MessageBox.Show("Unable to locate type " + xElem.Attributes["Module"].Value, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                {
                    CIM_Processor NewProc = Activator.CreateInstance(FoundType, xElem, NamedObjects, Repository) as CIM_Processor;
                    Processors.Add(NewProc);
                    NewProc.ProgressCheck.Bounds = new Rectangle(21, TopLocation, 216, 18);
                    TopLocation += 36;
                    this.Height = (this.Height - DisplayRectangle.Height) + NewProc.ProgressCheck.Bottom + 9;
                }
            }
        }

        /// <summary>
        /// Hanlde the selection of a sharepoint folder
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSharepointFolder_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select a file within the sharepoint folder...";
                oFd.Filter = "Any file (*.*)|*.*";
                if (oFd.ShowDialog() == DialogResult.OK)
                    lblSharepointFolderName.Text = Path.GetDirectoryName(oFd.FileName);
            }
        }

        /// <summary>
        /// Handle opening of a new model
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOpen_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select a CIM model to open...";
                oFd.Filter = "CIM SQLite Index (CIM*.SQLite)|CIM*.SQLite";
                if (oFd.ShowDialog() == DialogResult.OK)
                {
                    SelectedModel = oFd.FileName;
                    MM_UserInterface_Helpers.InterpretModel(oFd.FileName, cmbModelClass, dtValidStart, txtModelIteration, cmbModelCategory, Models, cmbLastModel);
                    dtExport.Value = new FileInfo(oFd.FileName).LastWriteTime;
                    
                    //Determine our model path
                    if (Path.GetDirectoryName(oFd.FileName).Contains(':'))
                        txtModelPath.Text = Regex.Replace(Path.GetDirectoryName(oFd.FileName), "(?<drive>[a-zA-Z]):", "\\\\" + MM_Type_Converter.TitleCase(Environment.MachineName) + "\\${drive}$$");
                    else
                        txtModelPath.Text = Path.GetDirectoryName(oFd.FileName);
                    lblModelName.Text = Path.GetFileNameWithoutExtension(oFd.FileName);
                }
            }
        }

        /// <summary>
        /// Now, add in our new model
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOk_Click(object sender, EventArgs e)
        {
            btnOk.Enabled = false;
            Model = new MM_Database_Model();
            Model.Name = Path.GetFileNameWithoutExtension(SelectedModel);
            Model.Db = Repository.Db;
            Model.Repository = Repository;
            Model.ModelClass = cmbModelClass.Text;
            Model.FullClass = txtModelIteration.Text;
            Model.ExportedOn = dtExport.Value;
            Model.ValidStart = dtValidStart.Value;
            Model.ModelCategory = cmbModelCategory.Text;
            Model.TargetSPFolder = lblSharepointFolderName.Text;

            if (String.IsNullOrEmpty(Model.ModelCategory))
            {
                MessageBox.Show("Please select a model category (e.g., current, OE future).", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }

            //If we're copying to the share, do so.
            if (sender == btnCopyAdd)
            {
                Model.FullPath = txtModelPath.Text;
                Model.AlternatePath = @"[ModelPath]" + Model.FullClass;
                Directory.CreateDirectory(Model.AlternatePath);
                foreach (String FileToCopy in ".xml,.CimIndex,.SQLite,-BreakerToBreakers.xml".Split(','))
                    File.Copy(Path.Combine(txtModelPath.Text, Path.GetFileNameWithoutExtension(Model.Name) + FileToCopy), Path.Combine(Model.AlternatePath, Path.GetFileNameWithoutExtension(Model.Name) + FileToCopy));
                File.Copy(Path.Combine(txtModelPath.Text, "Contingencies.SQLite"), Path.Combine(Model.AlternatePath, "Contingencies.SQLite"));
            }
            else
                Model.FullPath = txtModelPath.Text;
            Model.InitiateLoading();

            NamedObjects.Add("Model", Model);


            //Pull in our CimIndex file for overall information
            Dictionary<String, int> Vals = new Dictionary<string, int>();
            UInt64 MaxTEID = 0;
            using (FileStream fS = new FileStream(Path.Combine(Model.FullPath, Model.Name + ".CimIndex"), FileMode.Open))
            {
                List<byte> inHeader = new List<byte>();
                byte inByte;
                while ((inByte = (byte)fS.ReadByte()) != 10)
                    inHeader.Add(inByte);
                String VersionInfo = Encoding.UTF8.GetString(inHeader.ToArray());
                using (GZipStream gS = new GZipStream(fS, CompressionMode.Decompress))
                using (StreamReader sRd = new StreamReader(gS))
                    while (!sRd.EndOfStream)
                    {
                        String[] InLine = sRd.ReadLine().Split(':');
                        if (InLine.Length == 2)
                        {
                            int InVal = Convert.ToInt32(InLine[1]);
                            Vals.Add(InLine[0], InVal);
                            if (InLine[0] == "TEIDs")
                                for (int a = 0; a < InVal; a++)
                                    MaxTEID = Math.Max(MaxTEID, Convert.ToUInt64(sRd.ReadLine().Split(',')[0]));
                            else if (Array.IndexOf("Attributes,Types,TypeToTypes,TypeFromTypes,TEIDs,Elements".Split(','), InLine[0]) != -1)
                                for (int a = 0; a < InVal; a++)
                                    sRd.ReadLine();
                        }
                    }
            }

            Model.MaxTEID = MaxTEID;
            Model.MaxMRID = Vals["Maximum mRID"];
            Model.ElementCount = Vals["Elements"];
            Model.TEIDCount = Vals["TEIDs"];
            Model.PriorModel = (cmbLastModel.SelectedItem as DataRowView)[1] as MM_Database_Model;
            Model.WriteRdb(Repository);
            btnRefresh_Click(btnRefresh, EventArgs.Empty);
            MessageBox.Show("Model " + Model.Name + " added to database.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            btnOk.Enabled = true;
        }

        /// <summary>
        /// Run our selected options
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExec_Click(object sender, EventArgs e)
        {
            //If our thread is running, abort it, otherwise execute.
            if (ProcessManager == null)
            {
                //Start by locking down our elements
                foreach (CIM_Processor Processor in Processors)
                    Processor.ProgressCheck.Enabled = false;
                btnProcess.Image = Resources.StopHS;
                btnProcess.Text = "Abort Processing";
                ThreadPool.QueueUserWorkItem(new WaitCallback(RunProcessManager));
            }
            else
            {
                ProcessManager.Abort();
                btnProcess.Image = Resources.PlayHS;
                btnProcess.Text = "Begin Processing";

            }
        }

        #region Process Management loop
        /// <summary>
        /// This is the main loop for our process manager
        /// </summary>
        /// <param name="state"></param>
        private void RunProcessManager(object state)
        {
            ProcessManager = Thread.CurrentThread;

            //First, build our list of items
            int MinIndex = int.MaxValue, MaxIndex = int.MinValue;
            foreach (CIM_Processor Processor in Processors)
            {
                MinIndex = Math.Min(MinIndex, Processor.Step);
                MaxIndex = Math.Max(MaxIndex, Processor.Step);
            }

            //Now, wait until our model is ready
            if (!Model.EnsureOpened())
                return;


            //Now, go step by step, and execute our steps
            for (int a = MinIndex; a <= MaxIndex; a++)
            {
                foreach (CIM_Processor Process in Processors)
                    if (Process.Step == a && Process.ProgressCheck.Checked)
                        Process.InitiateProcess(Repository.Db);
                //Check to make sure we don't have an abort flag
                if (NamedObjects.ContainsKey("HaltProcessing") && (bool)NamedObjects["HaltProcessing"])
                {
                    ProcessManager = null;
                    return;
                }
            }

            //Now, close down our process manager            
            btnProcess.Image = Resources.PlayHS;
            btnProcess.Text = "Begin Processing";
            ProcessManager = null;
        }
        #endregion

        #region Maintenance activities
        /// <summary>
        /// Execute our command based on request
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExecute_Click(object sender, EventArgs e)
        {
            //Start by opening the model
            MM_Database_Model CurModel = cmbModel.SelectedItem as MM_Database_Model;

            //First, make sure our selected model is opened
            if (CurModel == null)
                return;
            if (cmbAction.Text != "Re-Index Models" && cmbAction.Text != "Repair font sizes in one-lines" && cmbAction.Text != "Open database in direct mode" && cmbAction.Text != "Recompute substation county data" && cmbAction.Text != "Perform full validation (MM Model, CRR, Validation)" && !CurModel.EnsureOpened())
                return;

            if (cmbAction.Text == "Repair font sizes in one-lines")
                using (OpenFileDialog oFd = new OpenFileDialog())
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Filter = oFd.Filter = "Macomber Map One-Line (*.mm_oneline)|*.mm_oneline";
                    sFd.Title = "Select a target directory for the updated one-lines";
                    oFd.Title = "Select a one-line from the source directory";
                    oFd.InitialDirectory = CurModel.FullPath;
                    if (oFd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                        return;
                    sFd.FileName = oFd.FileName;
                    if (sFd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                        return;                    
                    String TargetPath = Path.GetDirectoryName(sFd.FileName);
                    foreach (FileInfo fI in new DirectoryInfo(Path.GetDirectoryName(oFd.FileName)).GetFiles("*.mm_Oneline"))
                    using (StreamReader ReadStream = fI.OpenText())
                    {
                        XmlDocument xDoc = new XmlDocument();
                        String InXml = ReadStream.ReadToEnd();
                        xDoc.LoadXml(InXml);
                        foreach (XmlAttribute xFont in xDoc.SelectNodes("//@Font"))
                            xFont.Value = "Arial Unicode MS, 9pt";
                        foreach (XmlElement xElem in xDoc.SelectNodes("//Descriptor"))
                            xElem.Attributes.Append(xElem.OwnerDocument.CreateAttribute("Font")).Value = "Arial Unicode MS, 9pt";
                        using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(TargetPath, fI.Name), new UTF8Encoding(false)) { Formatting = Formatting.Indented})
                        xDoc.WriteContentTo(xW);
                    }
                }
                        
                          
                       

            if (cmbAction.Text == "Produce validation summary")
            {
                StringBuilder sB = new StringBuilder();
                foreach (KeyValuePair<string, List<string>> kvp in CIM_OneLineValidator.ImportValidationSummaries(Repository, CurModel))
                {
                    sB.AppendLine(kvp.Key + ":");
                    foreach (String str in kvp.Value)
                        sB.AppendLine("\t" + str);
                    sB.AppendLine();
                }
                Clipboard.SetText(sB.ToString());
                MessageBox.Show(sB.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (cmbAction.Text == "Re-Index Models")
            {
                CIM_Model_Maintenance.ReIndexModels(Repository);                      
                return;
            }

            if (cmbAction.Text == "Load Substation coordinates")
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Select an updated substation coordinate worksheet";
                    oFd.Filter = "Macro-enabled Excel 2007 spreadsheet (*.xlsm)|*.xlsm";
                    if (oFd.ShowDialog() == DialogResult.OK)
                        CIM_Model_Maintenance.LoadSubstationCoordinates(Repository, oFd.FileName, CurModel);
                }


            if (cmbAction.Text == "Load District files")
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select a shape file for importing";
                oFd.Filter = "Shapefile (*.shp)|*.shp";
                if (oFd.ShowDialog() == DialogResult.OK)
                    CIM_Model_Maintenance.LoadDistrictFiles(Path.GetDirectoryName(oFd.FileName));                
            }

            if (cmbAction.Text == "Re-export OTS substations with synchroscopes")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Select a folder to save the OTS one-lines";
                    sFd.FileName = "OTS-OneLine_dset.ddl";
                    if (sFd.ShowDialog() == DialogResult.OK)
                        CIM_Model_Maintenance.ExportOTSSynchroscopeStations(Path.GetDirectoryName(sFd.FileName), Repository, CurModel);                                                              
                }


            if (cmbAction.Text == "Write equipment operatorship")
                CIM_Model_Maintenance.WriteEquipmentOperatorship(CurModel);


            if (cmbAction.Text == "Re-validate substations with jumpers")
                CIM_Model_Maintenance.RevalidateSubstationsWithJumpers(Repository, CurModel);
            else if (cmbAction.Text == "Recompute substation county data")
                CIM_Model_Maintenance.RecomputeCountyMembership(Repository);

            else if (cmbAction.Text == "Clear actively-assigned one-lines")
                using (DbCommand dCmd = Db.CreateCommand("DELETE FROM MM_DATABASE_ASSIGNMENT WHERE MODEL=:0 AND COMPLETEDON IS NULL", CurModel.PriorModel.ID))
                    dCmd.ExecuteNonQuery();
            else if (cmbAction.Text == "Roll back model one-line validation")
                CIM_Model_Maintenance.UndoModelValidation(Repository, CurModel);
            else if (cmbAction.Text == "Update one-line orientations")
                CIM_Model_Maintenance.UpdateOneLineOrientations(Repository, CurModel);
            else if (cmbAction.Text == "Write Macomber Map One-Lines")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Select the target directory for the MM One-Lines:";
                    sFd.Filter = "Macomber Map One-line (Sub.MM_OneLine)|Sub.MM_OneLine";
                    if (sFd.ShowDialog() == DialogResult.OK)
                        MM_Database_OneLine.WriteOneLines(Path.GetDirectoryName(sFd.FileName), CurModel, Repository);
                }


            else if (cmbAction.Text == "Apply incremental file")
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Open incremental file";
                    oFd.Filter = "Incremental file (*.xml)|*.xml";
                    if (oFd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                    {
                        XmlDocument xDoc = new XmlDocument();
                        xDoc.Load(oFd.FileName);
                        CurModel.LoadIncrementalFile(xDoc, false);
                    }));
                }
            else if (cmbAction.Text == "Generate new XML with incremental changes")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Select the target filename for the model with incremental changes";
                    sFd.Filter = "CIM model (CIM*.xml)|CIM*.xml";
                    sFd.FileName = Path.Combine(Path.GetDirectoryName(CurModel.ModelFullPath), Path.GetFileNameWithoutExtension(CurModel.ModelFullPath) + "-WithIncremental.xml");
                    if (sFd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { CurModel.WriteModelWithIncrementalUpdates(sFd.FileName); }));
                }

            else if (cmbAction.Text == "Rebuild Images")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Select the target directory for the one-line images:";
                    sFd.Filter = "One-line image (*.png)|Sub.png";
                    if (sFd.ShowDialog() == DialogResult.OK)
                        MM_Database_OneLine.WriteOneLineImages(Path.GetDirectoryName(sFd.FileName), CurModel, Repository, chkEmail.Checked);
                }
            else if (cmbAction.Text.StartsWith("Export"))
                ExportOneLine(cmbAction.Text.Substring(cmbAction.Text.IndexOf(' ') + 1), CurModel);
            else if (cmbAction.Text == "List Discrepancies")
                ListDiscrepancies(CurModel);
            else if (cmbAction.Text == "Load Synchroscopes")
                LoadSynchroscopes(CurModel);
            else if (cmbAction.Text == "Load Synchrocheck Relays")
                LoadSynchrocheckRelays(CurModel);
            else if (cmbAction.Text == "Generate PUN Images")
                GeneratePUNImages(CurModel);
            else if (cmbAction.Text == "Write Contingency Excel Spreadsheet")
                CIM_BreakerToBreaker.WriteExcelSpreadsheet(new List<string>(), CurModel, Repository);
            else if (cmbAction.Text == "Open database in direct mode" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
            {
                String IndexPath = Path.Combine(CurModel.FullPath, CurModel.Name + ".CimIndex");
                if (!File.Exists(IndexPath))
                    MessageBox.Show("Unable to locate the model's Cim Index!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                else if (CurModel is CIM_DirectModel && CurModel.State == MM_Database_Model.enumModelState.Completed)
                {
                }
                else
                    CurModel = CIM_DirectModel.OpenModel(CurModel, Repository, Models, cmbModel);

                //Try and find our owner form
                WeifenLuo.WinFormsUI.Docking.DockPanel TargetPanel = null;
                Form FormToCheck = this.ParentForm;
                do
                {
                    foreach (Control ctl in FormToCheck.Controls)
                        if (ctl is WeifenLuo.WinFormsUI.Docking.DockPanel)
                            TargetPanel = ctl as WeifenLuo.WinFormsUI.Docking.DockPanel;
                    FormToCheck = FormToCheck.ParentForm;
                } while (TargetPanel == null && FormToCheck != null);

                //Pop up our direct editor
                User_Interfaces.CIM_Browser.frmCIMBrowser NewForm = new User_Interfaces.CIM_Browser.frmCIMBrowser(CurModel);
                if (TargetPanel == null)
                {
                    NewForm.CIM.ClearElements();
                    NewForm.Show();
                }
                else
                {
                    NewForm.Dock = DockStyle.Right;
                    NewForm.Show(TargetPanel);
                }
                this.Focus();
            }

            if (cmbAction.Text == "Process Blackstart Corridor file")
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Open blackstart corridor information";
                    oFd.Filter = "XML file (*.xml)|*.xml";
                    if (oFd.ShowDialog() == DialogResult.OK)
                        foreach (String FileName in oFd.FileNames)
                        {
                            XmlDocument xDoc = new XmlDocument();
                            xDoc.Load(FileName);
                            MM_Blackstart_Corridor NewCorridor = new MM_Blackstart_Corridor(xDoc, CurModel);
                            xDoc.Save(Path.Combine(Path.GetDirectoryName(FileName), Path.GetFileNameWithoutExtension(FileName) + "-Updated" + Path.GetExtension(FileName)));
                        }
                }

          

            if (cmbAction.Text == "Write Breaker-To-Breaker Deltas" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Save breaker-to-breaker deltas...";
                    sFd.Filter = "Macomber Map b2b deltas(*-BreakerToBreakerDeltas.xml)|*-BreakerToBreakerDeltas.xml";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath ,CurModel.NameWithoutExtension + "-BreakerToBreakerDeltas.xml");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_ContingencyDeltaReport), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                }
            
            if (cmbAction.Text == "Write RAS Displays")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "RAS Display exports";
                    sFd.Filter = "RAS Display (RAS_Display.ddl)|RAS_Display.ddl";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath, "RAS_Display.ddl");
                    if (sFd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_RemedialActionSchemeDisplay), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName));
                }

            if (cmbAction.Text == "Write MM Model File" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Save Macomber Map model information...";
                    sFd.Filter = "Macomber Map Model (*.MM_Model)|*.MM_Model";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath , CurModel.NameWithoutExtension + "-MacomberMap.MM_Model");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_MMExporter), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                }

            if (cmbAction.Text == "Produce GIS Shapefile of transmission network" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Save GIS shapefile data...";
                    sFd.Filter = "Shapefile Data (Network.Shp)|Network.shp";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath, CurModel.NameWithoutExtension + "-Network.shp");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_Shapefile_Exporter), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                }

            if (cmbAction.Text == "Produce RAS to Contingency list" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                CIM_Processor.ExecuteProcess(typeof(CIM_RASListGenerator), Repository, CurModel);
            if (cmbAction.Text == "Validate substation coordinates" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                CIM_Processor.ExecuteProcess(typeof(CIM_Coordinate_Validator), Repository, CurModel);

            if (cmbAction.Text == "Write Kiosk/Mobile app unit data" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Save Kiosk / Mobile app information...";
                    sFd.Filter = "Kiosk/mobile app information (*.Xml)|*.Xml";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath, CurModel.NameWithoutExtension + "-ModelUnitData.xml");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_Kiosk_Exporter), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                }
           
            if (cmbAction.Text == "Write CRR Model" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
                {
                    sFd.Title = "Save CRR Connected Trace information...";
                    sFd.Filter = "CRR Connected Trace information (*.CRR_Xml)|*.CRR_Xml";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath ,CurModel.NameWithoutExtension + "-ConnectedTraces.CRR_Xml");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_CRRTracer), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                }

            if (cmbAction.Text == "Write CRR KML Export file" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
                using (SaveFileDialog sFd = new SaveFileDialog())
            {
                 sFd.Title = "Save CRR XML Integrater model information...";
                    sFd.Filter = "CRR XML Integrater model information (*.MM_CRR)|*.MM_CRR";
                    sFd.FileName = Path.Combine(CurModel.ModelFullPath ,CurModel.NameWithoutExtension + "-CRRExport.MM_CRR");
                    sFd.InitialDirectory = CurModel.ModelFullPath;
                    if (cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)" || sFd.ShowDialog() == DialogResult.OK)
                        CIM_Processor.ExecuteProcess(typeof(CIM_CRRExporter), Repository, CurModel, "TargetFolder", Path.GetDirectoryName(sFd.FileName), "TargetFileName", Path.GetFileName(sFd.FileName));
                
            }

            if (cmbAction.Text == "Validate and Export" || cmbAction.Text == "Perform full validation (MM Model, CRR, Validation)")
            {
                XmlElement xExport = Repository.xConfig.SelectSingleNode("Configuration/OneLineExport") as XmlElement;
                CIM_Processor.ExecuteProcess(typeof(CIM_OneLineValidator), Repository, CurModel, "DDLDirectory", xExport.Attributes["DDLDirectory"].Value, "EMSDirectory", xExport.Attributes["EMSDirectory"].Value, "TargetFolder", xExport.Attributes["ExportDirectory"].Value, "OLImageDirectory", xExport.Attributes["OLImageDirectory"].Value, "FGImageDirectory", xExport.Attributes["ImageDirectory"].Value, "MMXmlDirectory", xExport.Attributes["MMXmlDirectory"].Value);
            }
        }

        /// <summary>
        /// Export our collection of PUN images
        /// </summary>
        /// <param name="CurModel"></param>
        private void GeneratePUNImages(MM_Database_Model CurModel)
        {
            using (FolderBrowserDialog fBd = new FolderBrowserDialog())
            {
                fBd.RootFolder = Environment.SpecialFolder.Desktop;
                fBd.Description = "Select the path for PUN displays...";
                if (fBd.ShowDialog() != DialogResult.OK)
                    return;
                foreach (CIM_Element PUNSub in CurModel.PUNStations)
                    using (OracleCommand oCmd = new OracleCommand("SELECT * FROM MM_DATABASE_ONELINE WHERE UPPER(RDFID)='" + PUNSub.rdfID.ToString().ToUpper() + "' AND MODELS LIKE '%," + CurModel.ID.ToString() + ",%'", Db.Database as OracleConnection))
                    using (OracleDataReader oRd = oCmd.ExecuteReader())
                        if (oRd.Read())
                        {
                            MM_Database_OneLine OL = new MM_Database_OneLine(oRd, Repository);
                            MM_Image_Exporter.ExportImage(OL, CurModel, Path.Combine(fBd.SelectedPath, MM_Type_Converter.TitleCase(PUNSub.LongName) + ".png"), ImageFormat.Png, CurModel.LocateExport(OL), Repository.Db.GetLastAssignment(OL));
                            Application.DoEvents();
                        }
                MessageBox.Show("Export completed.", Application.ProductName, MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// Load our synchroscopes
        /// </summary>
        /// <param name="CurModel"></param>
        private void LoadSynchroscopes(MM_Database_Model CurModel)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Open synchroscope file...";
                oFd.Filter = "Synchroscope file (*.txt)|*.txt";
                if (oFd.ShowDialog() != DialogResult.OK)
                    return;
                ThreadPool.QueueUserWorkItem(new WaitCallback(Db.UploadSynchroscopes), new object[] { oFd.FileName, CurModel });
            }
        }

        /// <summary>
        /// Load our synchrocheck relays
        /// </summary>
        /// <param name="CurModel"></param>
        private void LoadSynchrocheckRelays(MM_Database_Model CurModel)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Open synchrocheck relay file...";
                oFd.Filter = "Synchrocheck relay file (*.txt)|*.txt";
                if (oFd.ShowDialog() != DialogResult.OK)
                    return;
                ThreadPool.QueueUserWorkItem(new WaitCallback(Db.UploadSynchrocheckRelays), new object[] { oFd.FileName, CurModel });
            }
        }
     


        /// <summary>
        /// Export all one-lines
        /// </summary>
        /// <param name="Command"></param>
        /// <param name="CurModel"></param>
        private void ExportOneLine(String Command, MM_Database_Model CurModel)
        {
            //Determine our target directory, and ensure that our structure is in place
            XmlElement xExport = Repository.xConfig.SelectSingleNode("Configuration/OneLineExport") as XmlElement;
            Dictionary<String, Object> OutDic = new Dictionary<string, object>();
            OutDic.Add("Model", CurModel);
            OutDic.Add("PriorModel", CurModel.PriorModel);
            CIM_OneLineValidator OLValidator = new CIM_OneLineValidator(xExport, OutDic, Repository);

            String TargetDirectory = OLValidator.ProcessString(xExport.Attributes["TargetDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String DDLDirectory = OLValidator.ProcessString(xExport.Attributes["DDLDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String ExportDirectory = OLValidator.ProcessString(xExport.Attributes["ExportDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String OLImageDirectory = OLValidator.ProcessString(xExport.Attributes["OLImageDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String ImageDirectory = OLValidator.ProcessString(xExport.Attributes["ImageDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String EMSDirectory = OLValidator.ProcessString(xExport.Attributes["EMSDirectory"].Value.Replace("[CurModel]", CurModel.Name));
            String MMXmlDirectory = xExport.HasAttribute("MMXmlDirectory") ? OLValidator.ProcessString(xExport.Attributes["MMXmlDirectory"].Value) : null;
            CIM_OneLineValidator.ExportOneLine(Command, CurModel, TargetDirectory, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, ImageDirectory, Db, Repository, chkEmail.Checked, xExport.Attributes["SMTPServer"].Value, OLValidator.ProcessString(xExport.Attributes["Subject"].Value), new MM_String_Collection(xExport.Attributes["MailRecipients"].Value.Split(',')), MMXmlDirectory, OLValidator.ProcessLog,true);
            MessageBox.Show("Completed export.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        /// <summary>
        /// Produce our list of potential discrepencies
        /// </summary>
        /// <param name="CurModel"></param>
        private void ListDiscrepancies(MM_Database_Model CurModel)
        {
            //Go through the list of traces, and determine the connected trace statuses, keeping the list.
            SortedDictionary<CIM_Trace, List<CIM_Trace>> ConnectedTraces = new SortedDictionary<CIM_Trace, List<CIM_Trace>>();
            Dictionary<CIM_RdfID, int> Voltages = new Dictionary<CIM_RdfID, int>();
            foreach (CIM_Element Elem in CurModel.ElementsOfType("cim:ACLineSegment"))
                Voltages.Add(Elem.rdfID, Convert.ToInt32(float.Parse(Elem.VoltageLevel.Name.Split(' ')[0])));

            List<CIM_Trace> ThisTrace;
            int Voltage;
            bool TestLine;
            foreach (CIM_Trace Trace in CurModel.Traces.Values)
            {
                if (Trace.Name.StartsWith("S"))
                {
                    TestLine = false;
                    foreach (CIM_RdfID rdfID in Trace.rdfIDs)
                        if (Voltages.TryGetValue(rdfID, out Voltage) && Voltage >= 138)
                            TestLine = true;


                    if (TestLine)
                        foreach (CIM_RdfID rdfID in Trace.rdfIDs)
                            foreach (CIM_Trace Trace2 in CurModel.TraceLinkages[rdfID])
                                if (!Trace2.Equals(Trace) && Trace2.Name.StartsWith("S"))
                                {
                                    TestLine = false;
                                    foreach (CIM_RdfID rdfID2 in Trace2.rdfIDs)
                                        if (Voltages.TryGetValue(rdfID2, out Voltage) && Voltage >= 138)
                                            TestLine = true;

                                    if (TestLine)
                                        if (ConnectedTraces.TryGetValue(Trace, out ThisTrace))
                                            if (ThisTrace.Contains(Trace2))
                                            { }
                                            else
                                                ThisTrace.Add(Trace2);
                                        else if (ConnectedTraces.TryGetValue(Trace2, out ThisTrace))
                                            if (ThisTrace.Contains(Trace))
                                            { }
                                            else
                                                ThisTrace.Add(Trace);
                                        else
                                        {
                                            ThisTrace = new List<CIM_Trace>();
                                            ThisTrace.Add(Trace2);
                                            ConnectedTraces.Add(Trace, ThisTrace);
                                        }
                                }
                }
            }

            //Now, produce our list
            using (StreamWriter sW = new StreamWriter(Path.Combine(CurModel.ModelFullPath, CurModel.Name + "-PotentialOpenEndedCtgs.txt")))
                foreach (KeyValuePair<CIM_Trace, List<CIM_Trace>> kvp in ConnectedTraces)
                {
                    sW.Write(kvp.Key);
                    foreach (CIM_Trace trace in kvp.Value)
                        sW.Write("," + trace.Name);
                    sW.WriteLine();
                }
            MessageBox.Show("Export completed: " + ConnectedTraces.Count.ToString("#,##0") + " potential discrepancies.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);

            /*
            Dictionary<CIM_RdfID, CIM_Element> Lines = new Dictionary<CIM_RdfID, CIM_Element>();
            foreach (CIM_Element Line in CurModel.ElementsOfType("cim:ACLineSegment"))
                Lines.Add(Line.rdfID, Line);
            List<CIM_Element> Substations = new List<CIM_Element>();


            List<CIM_Trace> Exported = new List<CIM_Trace>();
            using (StreamWriter sW = new StreamWriter(@"C:\users\mlegatt\desktop\" + CurModel.Name + "-StoSList.txt"))
            {
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                    if (Trace.Name.StartsWith("S"))
                    {
                        bool FoundUnique = false;
                        Exported.Add(Trace);
                        StringBuilder sB = new StringBuilder();
                        foreach (CIM_Trace Trace2 in Trace.ConnectedTraces)
                            if (Trace2.Name.StartsWith("S"))
                            {
                                if (!Exported.Contains(Trace2))
                                {
                                    FoundUnique = true;
                                    Exported.Add(Trace2);
                                }
                                sB.Append(Trace2.Name + ",");
                            }
                        if (sB.Length > 0 && !FoundUnique)
                        {
                            CIM_Element OutLine;
                            sW.WriteLine(Trace.Name + ": " + sB.ToString());
                            foreach (CIM_RdfID Line in Trace.rdfIDs)
                                if (Lines.TryGetValue(Line, out OutLine))
                                    foreach (CIM_Element LineSub in OutLine.ACSegmentStations)
                                        if (!Substations.Contains(LineSub))
                                            Substations.Add(LineSub);
                        }
                    }
                foreach (CIM_Element ElemSub in Substations)
                    sW.WriteLine(ElemSub.Name);
            }
            
            using (SaveFileDialog sFd = new SaveFileDialog())
            {
                MM_Database_Model CurModel = cmbModel.SelectedItem as MM_Database_Model;
                sFd.Title = "Save a list of potential open contingencies";
                sFd.InitialDirectory = CurModel.ModelFullPath;
                sFd.Filter = "CSV list (" + CurModel.Name + "-PotentialOpenB2Bs.csv)|" + CurModel.Name + "-PotentialOpenB2Bs.csv";
                sFd.FileName = CurModel.Name + "-PotentialOpenB2Bs.csv";
                if (sFd.ShowDialog() == DialogResult.OK)
                    ThreadPool.QueueUserWorkItem(new WaitCallback(ExportPotentialDiscrepancies), new object[] { sFd.FileName, CurModel });
            }*/
        }

        /// <summary>
        /// Export our potential discrepancies in its own thread
        /// </summary>
        /// <param name="state"></param>
        private void ExportPotentialDiscrepancies(object state)
        {
            object[] inObj = state as object[];
            MM_Database_Model CurModel = inObj[1] as MM_Database_Model;
            String OutDDL = Path.Combine(CurModel.ModelFullPath, CurModel.Name + "-Discrepancies");
            if (!Directory.Exists(OutDDL))
                Directory.CreateDirectory(OutDDL);

            using (StreamWriter sW = new StreamWriter((string)inObj[0], false, new UTF8Encoding(false)))
            {
                sW.WriteLine("\"Contingency\",\"Contingency Description\",\"Radial\",\"Substation\",\"Type\",\"Name\",\"rdfID\",\"Voltage\",\"TEID\",\"PSSe ID\",\"From Bus\",\"To Bus\"");
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                {
                    //Build our list of elements and their connections
                    Dictionary<CIM_Element, CIM_Element[]> LinkedElements = new Dictionary<CIM_Element, CIM_Element[]>();
                    foreach (CIM_Element Elem in Trace.Elements)
                    {
                        List<CIM_Element> Elems = new List<CIM_Element>(Elem.Elements);
                        Elems.AddRange(Elem.Nodes);
                        LinkedElements.Add(Elem, Elems.ToArray());
                    }
                }
            }
            if (chkEmail.Checked)
            {
                StringBuilder OutMessage = new StringBuilder();
                OutMessage.AppendLine(String.Format("This is the Macomber Map Loader 3's One-line validation Module, version {0}, running on {1}, by {2}\\{3} at {4}", Application.ProductVersion, Environment.MachineName, Environment.UserDomainName, Environment.UserName, DateTime.Now));
                OutMessage.AppendLine();
                OutMessage.AppendLine(String.Format("The one-line potentially-open breaker-to-breaker model was ran against CIM file {0}, and the results have been saved into {1}, along with additional diagnostics.", CurModel.Name, inObj[0]));
                XmlElement xExport = Repository.xConfig.SelectSingleNode("Configuration/OneLineB2BValidation") as XmlElement;
                MM_Email_Sending.SendEmail(xExport.Attributes["SMTPServer"].Value, xExport.Attributes["FromEmail"].Value, xExport.Attributes["FromName"].Value, xExport.Attributes["Subject"].Value, new MM_String_Collection(xExport.Attributes["MailRecipients"].Value.Split(',')), new MM_String_Collection(xExport.Attributes["CarbonCopies"].Value.Split(',')), new MM_String_Collection(xExport.Attributes["BlindCarbonCopies"].Value.Split(',')), OutMessage.ToString(), (string)inObj[0]);
            }
            MessageBox.Show("Validation completed.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// Update our current selection of models
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbModelCategory2_SelectedIndexChanged(object sender, EventArgs e)
        {
            cmbModel.Items.Clear();

            if (cmbModelCategory2.Text == "Direct")
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Select direct CIM Model...";
                    oFd.Filter = "CIM Index (*.CimIndex)|*.CimIndex";
                    if (oFd.ShowDialog() != DialogResult.OK)
                        return;
                    CIM_DirectModel CurModel2 = new CIM_DirectModel(oFd.FileName, true, Repository);
                    CurModel2.ID = this.Models.Count + 1;
                    this.Models.Add(CurModel2.ID, CurModel2);
                    Models.Remove(CurModel2.ID);
                    Models.Add(CurModel2.ID, CurModel2);
                    CurModel2.ModelLocation = MM_Database_Model.enumModelLocations.InPrimary;
                    CurModel2.Repository = Repository;
                    cmbModel.Items.Add(CurModel2);
                    cmbModel.SelectedItem = CurModel2;
                    while (CurModel2.State != MM_Database_Model.enumModelState.Completed)
                    {
                        Application.DoEvents();
                        Thread.Sleep(250);
                    }
                       //Try and find our owner form
                    WeifenLuo.WinFormsUI.Docking.DockPanel TargetPanel = null;
                    Form FormToCheck = this.ParentForm;
                    do
                    {
                        foreach (Control ctl in FormToCheck.Controls)
                            if (ctl is WeifenLuo.WinFormsUI.Docking.DockPanel)
                                TargetPanel = ctl as WeifenLuo.WinFormsUI.Docking.DockPanel;
                        FormToCheck = FormToCheck.ParentForm;
                    } while (TargetPanel == null && FormToCheck != null);
                    new MacomberMapSystem.Common.User_Interfaces.CIM_Browser.frmCIMBrowser(CurModel2).Show(TargetPanel);                    
                }
           
            SortedDictionary<int, MM_Database_Model> SortedModels = new SortedDictionary<int, MM_Database_Model>();
            foreach (MM_Database_Model Model in this.Models.Values)
                if (Model.ModelCategory == cmbModelCategory2.Text)
                    SortedModels.Add(-Model.ID, Model);
            cmbModel.Items.AddRange(SortedModels.Values.ToArray());
        }


        /// <summary>
        /// Refresh our collection of models
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRefresh_Click(object sender, EventArgs e)
        {
            cmbModel.SelectedIndex = -1;
            cmbModel.Items.Clear();
            new MM_Login_Process("Load models", Repository.LoadModels).Invoke();
            foreach (MM_Database_Model Mdl in Models.Values)
                if (!cmbModelCategory2.Items.Contains(Mdl.ModelCategory))
                    cmbModelCategory2.Items.Add(Mdl.ModelCategory);
            cmbModelCategory2_SelectedIndexChanged(btnRefresh, EventArgs.Empty);
        }

        /// <summary>
        /// Select a model by date
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            using (frmSelectModel SelModel = new frmSelectModel(Repository))
                if (SelModel.ShowDialog() == DialogResult.OK)
                {
                    cmbModelCategory2.SelectedItem = SelModel.SelectedModel.ModelCategory;
                    cmbModel.SelectedItem = SelModel.SelectedModel;
                }
        }

        #endregion

    

    }
}